<template>
  <div ref="theWrap" class="vue-tabulation" :class="gridClass">
    <!-- header -->
    <div ref="headerWrap" class="vue-tabulation__header-wrapper">
      <!-- 主内容 -->
      <table ref="header" :style="gridHeaderStyle" class="vue-tabulation__header">
        <colgroup>
          <col
            v-for="(col, index) in fixedNormalizeColumns"
            :key="index"
            :width="columnsWidthMap[col.prop] ? columnsWidthMap[col.prop] : col.width+ 'px'"
          />
          <col v-if="hasScrollableY" :width="gutterWidth + 'px'" />
        </colgroup>
        <thead>
          <tr>
            <th
              v-for="(item, index) in fixedNormalizeColumns"
              class="draggable"
              :data-prop="item.prop"
              ref="headerCell"
              :key="item.prop"
              :class="[computeCellClass(item)]">
              <template v-if="item.headerCellComp">
                <component
                  :is="item.headerCellComp"
                  :label="item.label"
                  :index="index"
                  :prop="item.prop"
                  :column="item"
                  :sort="sort"
                  v-on="$listeners"
                ></component>
              </template>
              <template v-else>{{ item.label }}</template>
            </th>
            <th v-if="hasScrollableY" class="vue-tabulation__gutter"></th>
          </tr>
        </thead>
      </table>
      <!-- header 左固定列 -->
      <table
        v-if="fixedLeftColumns.length"
        ref="header-fixed-left"
        class="vue-tabulation__header--fixed-left">
        <colgroup>
          <col
            v-for="(col, index) in fixedLeftColumns"
            :key="index"
            :width="columnsWidthMap[col.prop] ? columnsWidthMap[col.prop] : col.width+ 'px'"
          />
        </colgroup>
        <thead>
          <tr>
            <th
              v-for="(item, index) in fixedLeftColumns"
              class="draggable"
              :data-prop="item.prop"
              :key="item.prop"
              :class="[computeCellClass(item)]"
            >
              <template v-if="item.headerCellComp">
                <component
                  :is="item.headerCellComp"
                  :label="item.label"
                  :index="index"
                  :prop="item.prop"
                  :column="item"
                  :sort="sort"
                  v-on="$listeners"
                ></component>
              </template>
              <template v-else>{{ item.label }}</template>
            </th>
          </tr>
        </thead>
      </table>
      <!-- header 右固定列 -->
      <table
        v-if="fixedRightColumns.length"
        ref="header-fixed-right"
        class="vue-tabulation__header--fixed-right">
        <colgroup>
          <col
            v-for="(col, index) in fixedRightColumns"
            :key="index"
            :width="columnsWidthMap[col.prop] ? columnsWidthMap[col.prop] : col.width+ 'px'"
          />
          <col v-if="hasScrollableY" :width="gutterWidth + 'px'" />
        </colgroup>
        <thead>
          <tr>
            <th
              v-for="(item, index) in fixedRightColumns"
              :key="item.prop"
              :data-prop="item.prop"
              class="draggable"
              :class="[computeCellClass(item)]"
            >
              <template v-if="item.headerCellComp">
                <component
                  :is="item.headerCellComp"
                  :label="item.label"
                  :index="index"
                  :prop="item.prop"
                  :column="item"
                  :sort="sort"
                  v-on="$listeners"
                ></component>
              </template>
              <template v-else>{{ item.label }}</template>
            </th>
            <th v-if="hasScrollableY" class="vue-tabulation__gutter"></th>
          </tr>
        </thead>
      </table>
    </div>

    <!-- body -->
    <div
      ref="bodyWrap"
      class="vue-tabulation__body-wrapper"
      :style="bodyWrapStyle"
      @scroll="debounceScroll">

      <!-- 无数据 -->
      <div
        v-if="!normalizeDataSource.length"
        class="vue-tabulation__no-data"
        :style="{width: totalColWidth + 'px'}">
        暂无数据
      </div>


      <!-- 主内容区域 -->
      <table ref="body" class="vue-tabulation__body">
        <colgroup>
          <col
            v-for="(col, index) in fixedNormalizeColumns"
            :key="index"
            :width="columnsWidthMap[col.prop] ? columnsWidthMap[col.prop] : col.width+ 'px'"
          />
        </colgroup>
        <tbody>
          <tr :style="{ height: bodyTopPadding + 'px' }"></tr>
          <tr
            v-for="data in renderDataSource"
            :key="data[keyProp] || data['vue-tabulation_inner_index']"
            class="vue-tabulation-body-tr"
            :class="[
              data['vue-tabulation-checkbox'] ? 'vue-tabulation-body-tr--checked' : '',
              rowClassName && rowClassName(data, data['vue-tabulation_inner_index']),
            ]"
            @click="handleRowClick(data, $event)"
            @contextmenu="handleContextmenu(data, $event)">
            <td
              ref="bodyCell"
              v-for="column in fixedNormalizeColumns"
              :key="column.prop"
              :data-prop="column.prop"
              class="draggable"
              :class="[computeCellClass(column)]">
              <template v-if="column.bodyCellComp">
                <div class="cell">
                  <component
                    :is="column.bodyCellComp"
                    :formatter="column.bodyFormatter"
                    :value="data[column.prop]"
                    :prop="column.prop"
                    :index="data['vue-tabulation_inner_index']"
                    :column="column"
                    :data="column.data"
                    :row="data"
                    v-on="$listeners"
                  ></component>
                </div>
              </template>
              <template v-else>
                <div class="cell" :title="data[column.prop]">
                  {{
                  column.bodyFormatter
                  ? column.bodyFormatter(data[column.prop], data, column, data['vue-tabulation_inner_index'])
                  : data[column.prop]
                  }}
                </div>
              </template>
            </td>
          </tr>
          <tr :style="{ height: bodyBottomPadding + 'px' }"></tr>
        </tbody>
      </table>


      <!-- 左固定 -->
      <table
        v-if="fixedLeftColumns.length"
        ref="body-fixed-left"
        class="vue-tabulation__body--fixed-left"
        :style="{ transform: `translateX(${this.gridBodyScrollLeft}px)` }">
        <colgroup>
          <col
            v-for="(col, index) in fixedLeftColumns"
            :key="index"
            :width="columnsWidthMap[col.prop] ? columnsWidthMap[col.prop] : col.width+ 'px'"
          />
        </colgroup>
        <tbody>
          <tr :style="{ height: bodyTopPadding + 'px' }"></tr>
          <tr
            v-for="data in renderDataSource"
            :key="data['vue-tabulation_inner_index']"
            class="vue-tabulation-body-tr"
            :class="[
              data['vue-tabulation-checkbox'] ? 'vue-tabulation-body-tr--checked' : '',
              rowClassName && rowClassName(data, data['vue-tabulation_inner_index']),
            ]"
            @click="handleRowClick(data, $event)"
            @contextmenu="handleContextmenu(data, $event)"
          >
            <td
              v-for="column in fixedLeftColumns"
              :key="column.prop"
              :data-prop="column.prop"
              class="draggable"
              :class="[computeCellClass(column)]"
            >
              <template v-if="column.bodyCellComp">
                <div class="cell">
                  <component
                    :is="column.bodyCellComp"
                    :formatter="column.bodyFormatter"
                    :value="data[column.prop]"
                    :prop="column.prop"
                    :index="data['vue-tabulation-index'] - 1"
                    :column="column"
                    :data="column.data"
                    :row="data"
                    v-on="$listeners"
                  ></component>
                </div>
              </template>
              <template v-else>
                <div
                  class="cell"
                  :title="column.bodyFormatter ? column.bodyFormatter(data[column.prop], data, column, data['vue-tabulation_inner_index']) : data[column.prop]"
                >
                  {{ column.bodyFormatter ? column.bodyFormatter(data[column.prop], data, column, data['vue-tabulation_inner_index']) : data[column.prop] }}
                </div>
              </template>
            </td>
          </tr>
          <tr :style="{ height: bodyBottomPadding + 'px' }"></tr>
        </tbody>
      </table>
      <!-- 右固定 -->
      <table
        v-if="fixedRightColumns.length"
        ref="body-fixed-right"
        class="vue-tabulation__body--fixed-right"
        :style="{ transform: `translateX(${this.gridBodyScrollLeft - 1}px)` }">
        <colgroup>
          <col
            v-for="(col, index) in fixedRightColumns"
            :key="index"
            :width="columnsWidthMap[col.prop] ? columnsWidthMap[col.prop] : col.width+ 'px'"
          />
        </colgroup>
        <tbody>
          <tr :style="{ height: bodyTopPadding + 'px' }"></tr>
          <tr
            v-for="data in renderDataSource"
            :key="data['vue-tabulation_inner_index']"
            class="vue-tabulation-body-tr"
            :class="[
              data['vue-tabulation-checkbox'] ? 'vue-tabulation-body-tr--checked' : '',
              rowClassName && rowClassName(data, data['vue-tabulation_inner_index']),
            ]"
            @click="handleRowClick(data, $event)"
            @contextmenu="handleContextmenu(data, $event)"
          >
            <td
              v-for="column in fixedRightColumns"
              :key="column.prop"
              :data-prop="column.prop"
              class="draggable"
              :class="[computeCellClass(column)]"
            >
              <template v-if="column.bodyCellComp">
                <div class="cell">
                  <component
                    :is="column.bodyCellComp"
                    :formatter="column.bodyFormatter"
                    :value="data[column.prop]"
                    :prop="column.prop"
                    :index="data['vue-tabulation-index'] - 1"
                    :column="column"
                    :data="column.data"
                    :row="data"
                    v-on="$listeners"
                  ></component>
                </div>
              </template>
              <template v-else>
                <div
                  class="cell"
                  :title="column.bodyFormatter
                      ? column.bodyFormatter(data[column.prop], data, column, data['vue-tabulation_inner_index'])
                      : data[column.prop]"
                >
                  {{
                  column.bodyFormatter
                  ? column.bodyFormatter(data[column.prop], data, column, data['vue-tabulation_inner_index'])
                  : data[column.prop]
                  }}
                </div>
              </template>
            </td>
          </tr>
          <tr :style="{ height: bodyBottomPadding + 'px' }"></tr>
        </tbody>
      </table>
    </div>

    <!-- 合计行 -->
    <div v-if="showSummary" ref="summaryWrap" class="vue-tabulation__summary-wrapper">

      <!-- 主内容区域 -->
      <table ref="summary" class="vue-tabulation__summary" :style="gridHeaderStyle">
        <colgroup>
          <col
            v-for="(col, index) in fixedNormalizeColumns"
            :key="index"
            :width="columnsWidthMap[col.prop] ? columnsWidthMap[col.prop] : col.width+ 'px'"
          />
          <col v-if="hasScrollableY" class="vue-tabulation__gutter" :width="gutterWidth + 'px'" />
        </colgroup>
        <thead>
          <tr>
            <th
              v-for="(col, index) in fixedNormalizeColumns"
              :key="index"
              :class="[computeCellClass(col)]"
            >{{ summaryData[col["vue-tabulation-index"]] }}</th>
            <th v-if="hasScrollableY" class="vue-tabulation__gutter"></th>
          </tr>
        </thead>
      </table>

      <!-- 左固定区域 -->
      <table
        v-if="fixedLeftColumns.length"
        ref="summary-fixed-left"
        class="vue-tabulation__summary--fixed-left">
        <colgroup>
          <col
            v-for="(col, index) in fixedLeftColumns"
            :key="index"
            :width="columnsWidthMap[col.prop] ? columnsWidthMap[col.prop] : col.width+ 'px'"
          />
        </colgroup>
        <thead>
          <tr>
            <th
              v-for="(item, index) in fixedLeftColumns"
              :key="index"
              :class="[computeCellClass(item)]"
            >{{ summaryData[item["vue-tabulation-index"]] }}</th>
          </tr>
        </thead>
      </table>

      <!-- 右固定区域 -->
      <table
        v-if="fixedRightColumns.length"
        ref="summary-fixed-right"
        class="vue-tabulation__summary--fixed-right">
        <colgroup>
          <col
            v-for="(col, index) in fixedRightColumns"
            :key="index"
            :width="columnsWidthMap[col.prop] ? columnsWidthMap[col.prop] : col.width+ 'px'"
          />
          <col v-if="hasScrollableY" class="vue-tabulation__gutter" :width="gutterWidth + 'px'" />
        </colgroup>
        <thead>
          <tr>
            <th
              v-for="(item, index) in fixedRightColumns"
              :key="index"
              :class="[computeCellClass(item)]"
            >{{ summaryData[item["vue-tabulation-index"]] }}</th>
            <th v-if="hasScrollableY" class="vue-tabulation__gutter"></th>
          </tr>
        </thead>
      </table>
    </div>
  </div>
</template>
<script>
import interact from 'interactjs'
import { sortFixed, deepClone, rAF, debug } from './util'
import scrollBarWidth from './scrollbar-width'
import HeaderCellSort from './HeaderCellSort'
const log = debug({
  prefix: 'vue-tabulation:',
  disabled: true,
})
export default {
  name: 'vue-tabulation',

  props: {
    dataSource: {
      type: Array,
      default() {
        return []
      },
    },
    columns: {
      type: Array,
      required: true,
    },
    height: {
      type: Number,
    },
    border: {
      type: Boolean,
      default: true,
    },
    // 是否显示序号
    hasIndex: {
      type: Boolean,
      default: false,
    },
    // 是否显示复选框
    hasCheckbox: {
      type: Boolean,
      default: false,
    },
    // 设置每行的 class
    rowClassName: {
      type: Function,
    },
    // 是否显示合计列
    showSummary: {
      type: Boolean,
      default: false,
    },
    sumText: {
      type: String,
      default: '合计',
    },
    summaryMethod: {
      type: Function,
    },
    // 自定义索引
    indexMethod: {
      type: Function,
      default(index) {
        return index + 1
      },
    },
    // 指定那个 prop 可以作为唯一值
    keyProp: {
      type: String,
    },
    // 点击某行是否选中该行的复选框
    rowClickCheck: {
      type: Boolean,
      default: true,
    },
    // 右键某行是否选中该行的复选框
    rowContextmenuCheck: {
      type: Boolean,
      default: true,
    },
    // 是否可以拖曳改变宽度
    resizable: {
      type: Boolean,
      default: true,
    }
  },

  data() {
    return {
      // 是否mounted
      isMounted: false,
      // 是否有垂直滚动条
      hasScrollableY: false,
      // 是有有水平滚动条
      hasScrollableX: false,
      gridBodyWrapHeight: undefined,
      // 每个列的宽度
      colsWidth: [],
      columnsWidthMap: {},
      // 列总宽度
      totalColWidth: 0,
      // 滚动条宽度
      gutterWidth: scrollBarWidth(),
      // 表格 header 水平滚动条的位置
      gridBodyScrollLeft: 0,
      gridBodyScrollTop: 0,
      normalizeDataSource: [],
      // 全部复选框是否勾选
      checkbox: false,

      // 可视区渲染
      // 每行的高度
      rowHeight: 40,
      // 行的数量
      rowsLength: 0,

      // 是否排序
      sortable: false, // false, true, 'custom'
      // 排序字段
      sortProp: 'age',
      // 排序方式，升序/降序
      sortOrder: 'ascending', // ascending, descending, 'none'
      sort: {
        prop: '',
        order: 'desc',
      },

      // 排序方法
      sortMethods: null,
      // 原始整体高度
      initTheWrapHeight: 0,
      initTheWrapWidth: 0,
    }
  },

  computed: {
    normalizeColumns() {
      let self = this
      let result = this.columns.map(item => {
        if (!item.headerCellComp && !item.headerCellRender && item.sortable) {
          item.headerCellComp = HeaderCellSort
        }
        return item
      })
      if (this.hasCheckbox) {
        result.unshift({
          label: '#',
          prop: 'vue-tabulation-checkbox',
          width: 50,
          align: 'center',
          headerCellRender(h) {
            return h('input', {
              domProps: {
                type: 'checkbox',
                checked: self.checkbox,
              },
              on: {
                change: event => {
                  self.$emit('grid-checkbox-toggle', event.target.checked)
                },
              },
            })
          },
          bodyCellRender(h, { value, row }) {
            return h('input', {
              domProps: {
                type: 'checkbox',
                checked: value,
              },
              on: {
                change: event => {
                  self.$emit('grid-checkbox-change', event.target.checked, row['vue-tabulation-index'] - 1, row)
                },
              },
            })
          },
        })
      }
      if (this.hasIndex) {
        result.unshift({
          label: '#',
          prop: 'vue-tabulation-index',
          width: 50,
          align: 'center',
        })
      }
      return result
    },
    // 有固定列
    fixedNormalizeColumns() {
      let result = this.normalizeColumns.map(item => {
        // 如果列定义的表头没有定义 headerCellComp 且 定义了 render 函数，返回这个 render 函数
        if (!item.headerCellComp && item.headerCellRender) {
          item.headerCellComp = this.headerRenderFactory(item.headerCellRender)
        }
        // 表内容
        if (!item.bodyCellComp && item.bodyCellRender) {
          item.bodyCellComp = this.bodyRenderFactory(item.bodyCellRender)
        }
        return item
      })
      // 如果有左固定列，则把序号列和复选框列设置为左固定列
      if (this.normalizeColumns.some(item => item.fixed === 'left' || item.fixed === true)) {
        result = this.normalizeColumns.map(item => {
          if (item.prop === 'vue-tabulation-index' || item.prop === 'vue-tabulation-checkbox') {
            item.fixed = 'left'
          }
          return item
        })
      }
      result = sortFixed(result).map((item, index) => {
        item['vue-tabulation-index'] = index
        return item
      })
      return result
    },
    fixedLeftColumns() {
      return this.fixedNormalizeColumns.filter(item => item.fixed === 'left' || item.fixed === true)
    },
    fixedRightColumns() {
      return this.fixedNormalizeColumns.filter(item => item.fixed === 'right')
    },
    columnsLength() {
      return this.normalizeColumns.length
    },
    gridClass() {
      return {
        'vue-tabulation--border': this.border,
        'vue-tabulation--scrollable-y': this.hasScrollableY,
      }
    },
    bodyWrapStyle() {
      return {
        height: this.gridBodyWrapHeight ? this.gridBodyWrapHeight + 'px' : undefined,
      }
    },
    gridHeaderStyle() {
      return {
        transform: `translateX(-${this.gridBodyScrollLeft}px)`,
      }
    },
    checkedRows() {
      return this.normalizeDataSource.filter(row => row['vue-tabulation-checkbox'])
    },
    rowsHide() {
      if (this.rowHeight) {
        return Math.floor(this.gridBodyScrollTop / this.rowHeight)
      } else {
        return 0
      }
    },
    rowShowSize() {
      if (this.rowHeight) {
        return Math.ceil(this.gridBodyWrapHeight / this.rowHeight)
      } else {
        return 0
      }
    },
    rowRenderStart() {
      return this.rowsHide - 5 > 0 ? this.rowsHide - 5 : 0
    },
    rowRenderEnd() {
      let value = this.rowRenderStart + this.rowShowSize + 10
      return value > this.rowsLength ? this.rowsLength : value
    },
    renderDataSource() {
      return this.normalizeDataSource.filter((item, index) => {
        return index >= this.rowRenderStart && index < this.rowRenderEnd
      })
    },
    bodyTopPadding() {
      return this.rowRenderStart * this.rowHeight
    },
    bodyBottomPadding() {
      return (this.rowsLength - this.rowRenderEnd) * this.rowHeight
    },
    summaryData() {
      if (this.showSummary) {
        if (this.summaryMethod) {
          return this.summaryMethod({
            columns: this.fixedNormalizeColumns,
            data: this.normalizeDataSource,
          })
        } else {
          return this.defaultSummaryMethod({
            columns: this.fixedNormalizeColumns,
            data: this.normalizeDataSource,
          })
        }
      } else {
        return []
      }
    },
  },

  watch: {
    height() {
      this.initHeight()
    },
    dataSource: {
      immediate: true,
      deep: true,
      handler() {
        this.$nextTick(() => {
          this.initHeight()
        })
        this.rowsLength = this.dataSource.length
        this.normalizeDataSource = this.getNormalizeDataSource()
      },
    },
    fixedNormalizeColumns: {
      immediate: true,
      deep: true,
      handler() {
        this.$nextTick(() => {
          this.initColumnWidth()
          if (this.resizable) {
            this.bindResize()
          }
        })
      },
    },
    sort: {
      deep: true,
      handler() {
        this.normalizeDataSource = this.getNormalizeDataSource()
      },
    },
    checkedRows: {
      immediate: true,
      handler() {
        if (this.checkedRows.length === this.rowsLength && this.rowsLength) {
          this.checkbox = true
        } else {
          this.checkbox = false
        }
      },
    },
  },

  methods: {
    getNormalizeDataSource() {
      let data = deepClone(this.dataSource)
      if (this.sort.prop && this.sort.order) {
        data.sort((a, b) => {
          let order = this.sort.order === 'asc' ? 1 : -1
          let aValue = a[this.sort.prop]
          let bValue = b[this.sort.prop]
          if (typeof aValue === 'number' && typeof bValue === 'number') {
            return (aValue - bValue) * order
          } else {
            aValue = aValue ? String(aValue) : ''
            bValue = bValue ? String(bValue) : ''
            return aValue.localeCompare(bValue) * order
          }
        })
      }
      return data.map((item, index) => {
        item['vue-tabulation_inner_index'] = index
        if (this.hasIndex) {
          item['vue-tabulation-index'] = this.indexMethod(index)
        }
        if (this.hasCheckbox) {
          item['vue-tabulation-checkbox'] = false
        }
        return item
      })
    },
    headerRenderFactory(headerCellRender) {
      let parent = this
      return {
        name: 'table-header-cell',
        props: {
          label: {
            type: String,
            required: true,
          },
          index: {
            type: Number,
            required: true,
          },
          column: {
            type: Object,
            required: true,
          },
          sort: {
            type: Object,
            required: true,
          },
        },
        render(h) {
          let self = this
          return headerCellRender.bind(self)(h, {
            label: this.label,
            index: this.index,
            column: this.column,
            sort: this.sort,
            parent: parent,
          })
        },
      }
    },
    bodyRenderFactory(render) {
      let _this = this
      return {
        name: 'cellBody',
        props: {
          value: {
            required: true,
          },
          prop: {
            type: String,
            required: true,
          },
          index: {
            type: Number,
            required: true,
          },
          column: {
            type: Object,
            required: true,
          },
          row: {
            type: Object,
            required: true,
          },
        },
        render(h) {
          return render.bind(_this)(h, {
            value: this.value,
            prop: this.prop,
            index: this.index,
            column: this.column,
            row: this.row,
          })
        },
      }
    },
    // 滚动时触发更新
    debounceScroll: function(e) {
      this.scrollLeft(e)
      this.scrollTop(e)
    },
    scrollLeft: rAF(function(e) {
      this.gridBodyScrollLeft = e.target.scrollLeft
    }),
    scrollTop: rAF(function(e) {
      this.gridBodyScrollTop = e.target.scrollTop
    }),
    // 点击行触发
    handleRowClick(data, event) {
      this.$emit('row-click', data['vue-tabulation_inner_index'], data, event)
      if (this.rowClickCheck) {
        this.selectionToggle([data['vue-tabulation_inner_index']], undefined, event)
      }
    },
    // 右键行触发
    handleContextmenu(data, event) {
      this.$emit('row-contextmenu', data['vue-tabulation_inner_index'], data, event)
      if (this.rowContextmenuCheck) {
        this.selectionToggle([data['vue-tabulation_inner_index']], true, event)
      }
    },
    // 多选操作
    selectionAllToggle(value) {
      log('selectionAllToggle')
      this.normalizeDataSource = this.normalizeDataSource.map(item => {
        item['vue-tabulation-checkbox'] = value === undefined ? !item['vue-tabulation-checkbox'] : Boolean(value)
        return item
      })
      this.$emit('select-all', this.checkedRows)
      this.$emit('selection-change', this.checkedRows)
    },
    /**
     * 切换指定行的复选框选中状态
     * @param {array|number} rowIndexArr
     * @param {boolean} [checked]
     */
    selectionToggle(rowIndexArr, checked, event) {
      rowIndexArr = Array.isArray(rowIndexArr) ? rowIndexArr : [rowIndexArr]
      this.normalizeDataSource = this.normalizeDataSource.map((item, index) => {
        if (rowIndexArr.includes(index)) {
          item['vue-tabulation-checkbox'] = checked === undefined ? !item['vue-tabulation-checkbox'] : Boolean(checked)
        }
        return item
      })
      this.$emit('select', this.checkedRows, rowIndexArr[0], event)
      this.$emit('selection-change', this.checkedRows)
    },
    selectionChange(value, rowIndex) {
      if (!this.rowClickCheck) {
        this.selectionToggle([rowIndex], value)
      }
    },
    // 计算单元格样式
    computeCellClass(column) {
      let classObj = {}
      if (['left', 'center', 'right'].includes(column.align)) {
        classObj[`is-${column.align}`] = true
      }
      return classObj
    },
    // 默认合计行方法
    defaultSummaryMethod({ columns, data }) {
      const isAllNumber = arr => {
        if (!arr.length) return false
        return arr.every(item => typeof item !== 'boolean' && !isNaN(Number(item)))
      }
      return columns.map((item, index) => {
        let values = data.map(itemData => itemData[item.prop])
        if (index === 0) {
          return this.sumText
        } else if (isAllNumber(values)) {
          return values.reduce((prev, curr) => {
            return prev + Number(curr)
          }, 0)
        } else {
          return ''
        }
      })
    },
    // 计算内容的高度，是否有滚动条
    initHeight() {
      if (!this.isMounted || !this.columns.length) return false
      log('initHeight')
      let initsummaryWrapHeight = 0
      if (this.showSummary) {
        initsummaryWrapHeight = this.$refs.summaryWrap.offsetHeight
      }
      if (this.height) {
        this.gridBodyWrapHeight = this.height - this.initHeaderWrapHeight - initsummaryWrapHeight
        // 如果设置的高度小于表格本身的高度则会产生垂直滚动条并且表头固定
        if (this.height < this.$refs.body.offsetHeight) {
          this.hasScrollableY = true
        } else {
          this.hasScrollableY = false
        }
      } else {
        this.gridBodyWrapHeight = this.initBodyWrapHeight
        this.hasScrollableY = false
      }
      log('hasScrollableY', this.hasScrollableY)
      this.initRowHeight()
    },
    /**
     * 计算每列的宽度
     * 计算列的总宽度
     * 计算列的高度
     * 计算gutter的高度
     */
    initColumnWidth() {
      if (!this.isMounted || !this.columns.length) return false

      let bodyCells = this.$refs.headerCell || []

      this.fixedNormalizeColumns.map((column, index) => {
        let width = bodyCells[index] && bodyCells[index].offsetWidth
        this.columnsWidthMap[column.prop] = width
      })
      this.getTotalColWidth()
      // console.log('columnsWidthMap: ', this.columnsWidthMap)
    },
    getTotalColWidth () {
      this.totalColWidth = Object.keys(this.columnsWidthMap).reduce((acc, key) => acc + this.columnsWidthMap[key], 0)
    },
    // 获取每行的高度
    initRowHeight() {
      let row = this.$refs.body.querySelector('.vue-tabulation-body-tr')
      //   console.log('initRowHeight', row)
      if (row) {
        this.rowHeight = row.offsetHeight
      }
    },
    // 绑定改变大小
    bindResize () {
        interact('.draggable').resizable({
          edges: {right: true},
          inertia: true,
        }).on('resizemove', event => {
           const prop = event.target.dataset["prop"]
           this.changeSize(prop, event.rect.width)
        })

    },
    changeSize (prop, newWidth) {
      let oldWith = this.columnsWidthMap[prop]
      // 如果改变宽度后，列的总宽度仍大于表格总宽度，只改变拖拽列的宽度
      // console.log(this.totalColWidth - (oldWith - newWidth) - this.initTheWrapWidth)
      if (this.totalColWidth - (oldWith - newWidth) >= this.initTheWrapWidth) {
        this.columnsWidthMap = Object.assign({}, this.columnsWidthMap, {
          [prop]: newWidth
        })
      } else {
        // 存储改变宽度列之后的列
        let lastKeys = [];
        let idx = 0;
        Object.keys(this.columnsWidthMap).forEach((key, index) => {
          if (key === prop) {
            idx = index
          }
          if (idx  && index > idx) {
            lastKeys.push(key)
          }
        })
        log('lastKeys', lastKeys, 'diffWidth', (oldWith - newWidth))
        if (lastKeys.length) {
          let diffWidth = (oldWith - newWidth)
          if (diffWidth > 0) {
            diffWidth = diffWidth / lastKeys.length
            lastKeys.forEach(key => {
              let oldW = this.columnsWidthMap[key]
              this.columnsWidthMap[key] = oldW + diffWidth
            })
            this.columnsWidthMap = deepClone(this.columnsWidthMap)
          }
        }
      }
      this.getTotalColWidth()
    }
  },

  created() {
    log('created')
    // 复选框全选或者取消全选
    this.$on('grid-checkbox-toggle', this.selectionAllToggle)
    // 复选框改变
    this.$on('grid-checkbox-change', this.selectionChange)
  },

  mounted() {
    log('mounted-start')

    this.isMounted = true
    // 表格bodyWrap的初始的初始高度
    this.initBodyWrapHeight = this.$refs.bodyWrap.offsetHeight
    this.initTheWrapHeight = this.$refs.theWrap.offsetHeight
    this.initTheWrapWidth = this.$refs.theWrap.offsetWidth
    this.initHeaderWrapHeight = this.$refs.headerWrap.offsetHeight
    this.initHeight()
    this.initColumnWidth()

    log('mounted-end')
  },
  activated() {
    // 修复使用`keep-alive`时，切换路由，滚动条重置为之前的状态
    this.$refs.bodyWrap.scrollTop = this.gridBodyScrollTop
    this.$refs.bodyWrap.scrollLeft = this.gridBodyScrollLeft
  },
  updated() {
    log('updated')
  },
  beforeDestroy () {
    interact('.draggable').unset();
  }
}
</script>
<style lang="scss" src="./style.scss"></style>
